'''
Created on Mar 29, 2010

@author: jhlee
'''
from Number3D import *
from Matrix44 import *
from Quaternion import *

from OpenGL.GL import *
from OpenGL.GLU import *

 
class SceneNode:
    def __init__(self):
        self.parent = None
        self.name = ''
        
        self.position = Number3D(0, 0, 0)
        self.scale = NUMBER_3D_UNIT_SCALE.clone()
        self.orientation = Quaternion()
        
        self.derivedPosition = Number3D(0, 0, 0)
        self.derivedScale = Number3D(1,1,1)
        self.derivedOrientation = Quaternion()
        
        self.needParentUpdate = False
        self.needChildUpdate = False
        self.cachedTransformOutOfDate = True
        
        self.inheritOrientation = True
        self.inheritScale = True
        self.inheritPosition = True
        
        self.cachedTransform = Matrix44()
        
        self.children = []
        
        self.entity = None
        

    def setName(self, name):
        self.name = name
    def getName(self):
        return self.name
        
    def setParent(self, parent):
        assert(parent <> None)
        self.parent = parent
        self.needUpdate()
        
    def removeParent(self):
        self.parent = None
        
    def getParent(self):
        return self.parent
    
    def setInheritOrientation(self, inherit):
        self.inheritOrientation = inherit
        self.needUpdate()
    def getInheritOrientation(self):
        return self.inheritOrientation
    
    def setInheritScale(self, inherit):
        self.inheritScale = inherit
        self.needUpdate()
    def getInheritScale(self):
        return self.inheritScale
    
    def setInheritPosition(self, inherit):
        self.inheritPosition = inherit
        self.needUpdate()
    def getInheritPosition(self):
        return self.inheritPosition
        
    def needUpdate(self):
        self.needParentUpdate = True
        self.needChildUpdate = True
        self.cachedTransformOutOfDate = True
        
    def getFullTransform(self):
        if self.cachedTransformOutOfDate:
            self.cachedTransform.makeTransform(self.getDerivedPosition(),
                                               self.getDerivedScale(),
                                               self.getDerivedOrientation())
            self.cachedTransformOutOfDate = False
        
        return self.cachedTransform
    
    def setOrientation(self, q):
        self.orientation = q
        self.needUpdate()
    def getOrientation(self):
        return self.orientation
    
    def setPosition(self, pos):
        self.position = pos
        self.needUpdate()        
    def getPosition(self):
        return self.position
    
    def setScale(self, scale):
        self.scale.copyFrom(scale)
        self.needUpdate()
    def getScale(self):
        return self.scale
    
    def translate(self, d):
        # Relative to parent.
        self.position.add(d)
        self.needUpdate()
        
    def translateXYZ(self, x,y,z):
        self.position.addXYZ(x,y,z)
        self.needUpdate()
    
    def getDerivedPosition(self):
        if self.needParentUpdate:
            self.updateFromParent()
            
        return self.derivedPosition
    
    def getDerivedOrientation(self):    
        if self.needParentUpdate:
            self.updateFromParent()
        return self.derivedOrientation
    
    def getDerivedScale(self):
        if self.needParentUpdate:
            self.updateFromParent()
            
        return self.derivedScale
        
    def convertLocalToWorldPosition(self, pos, out):
        if self.needParentUpdate:
            self.updateFromParent()
        
        self.derivedOrientation.rotate(pos, out)
        out.multiply(out, self.derivedScale)
        out.add(self.derivedPosition)
        
        return out
    
    def setEntity(self, entity):
        self.entity = entity
    
    def addChild(self, node):
        self.children.append(node)
        node.setParent(self)
        
    def removeChild(self, node):
        self.children.remove(node)
        node.removeParent()

    def updateFromParent(self):
        if self.parent is not None:
            parentOrientation = self.parent.getDerivedOrientation()
            
            if self.inheritOrientation:
                self.derivedOrientation.copyFrom(parentOrientation)
                self.derivedOrientation.multiply(self.orientation)
            else:
                self.derivedOrientation.copyFrom(self.orientation)
               
               
            parentScale = self.parent.getDerivedScale()
            if self.inheritScale:
                self.derivedScale.copyFrom(parentScale)
                self.derivedScale.multiply(self.scale)
            else:                 
                self.derivedScale.copyFrom(self.scale)
                
                
            self.derivedPosition.copyFrom(self.position)
            self.derivedPosition.multiply(self.derivedScale)            
            self.derivedPosition = parentOrientation.rotate(self.derivedPosition)
            
            self.derivedPosition.add(self.parent.getDerivedPosition())
        else:
            self.derivedOrientation.copyFrom(self.orientation)
            self.derivedScale.copyFrom(self.scale)
            self.derivedPosition.copyFrom(self.position)
            
        self.cachedTransformOutOfDate = True
        self.needParentUpdate = False

    def render(self):
        #
        # Render children first
        #
        for c in self.children:
            c.render()
        
        if self.entity:    
            m = self.getFullTransform()
            glPushMatrix()
            glMultMatrixf(m.toArray())
                        
            self.entity.render()
                        
            glPopMatrix()

if __name__ == "__main__":
    n = SceneNode()
    n.translateXYZ(10,10,0)
    m = n.getFullTransform()
    m.debugDump()
    
    